// FEAT3: Finite Element Analysis Toolbox, Version 3
// Copyright (C) 2010 - 2020 by Stefan Turek & the FEAT group
// FEAT3 is released under the GNU General Public License version 3,
// see the file 'copyright.txt' in the top level directory for details.

#pragma once
#ifndef KERNEL_ASSEMBLY_MEAN_FILTER_ASSEMBLER_HPP
#define KERNEL_ASSEMBLY_MEAN_FILTER_ASSEMBLER_HPP 1

#include <kernel/base_header.hpp>
#include <kernel/lafem/mean_filter.hpp>
#include <kernel/assembly/linear_functional_assembler.hpp>
#include <kernel/assembly/interpolator.hpp>
#include <kernel/assembly/common_functionals.hpp>
#include <kernel/analytic/common.hpp>

namespace FEAT
{
  namespace Assembly
  {
    /**
     * \brief Mean Filter assembler class
     *
     * This class assembles an integral mean filter for a given finite element space.
     *
     * \author Peter Zajac
     */
    class MeanFilterAssembler
    {
    public:
      /**
       * \brief Assembles an intergral mean filter
       *
       * \param[out] vec_prim, vec_dual
       * The primal and dual vectors for the mean filter.
       *
       * \param[in] space
       * The finite element space for which the filter is to be assembled.
       *
       * \param[in] cubature_factory
       * A cubature factory for integration.
       */
      template<typename MemType_, typename DataType_, typename IndexType_, typename Space_, typename CubatureFactory_>
      static void assemble(
        LAFEM::DenseVector<MemType_, DataType_, IndexType_>& vec_prim,
        LAFEM::DenseVector<MemType_, DataType_, IndexType_>& vec_dual,
        const Space_& space, const CubatureFactory_& cubature_factory)
      {
        // allocate primal and dual vectors
        LAFEM::DenseVector<Mem::Main, DataType_, IndexType_> vec_v(space.get_num_dofs(), DataType_(0));
        LAFEM::DenseVector<Mem::Main, DataType_, IndexType_> vec_w(space.get_num_dofs(), DataType_(0));

        // create a constant 1-function and its corresponding force functional
        Analytic::Common::ConstantFunction<Space_::world_dim> one_func(Real(1));
        Assembly::Common::ForceFunctional<decltype(one_func)> one_force(one_func);

        // interpolate 1-function into vector v
        Assembly::Interpolator::project(vec_v, one_func, space);

        // assemble 1-function force into vector w
        Assembly::LinearFunctionalAssembler::assemble_vector(vec_w, one_force, space, cubature_factory);

        // convert mem types
        vec_prim.convert(vec_v);
        vec_dual.convert(vec_w);
      }

      /**
       * \brief Assembles an intergral mean filter
       *
       * \param[out] filter
       * The filter to be assembled.
       *
       * \param[in] space
       * The finite element space for which the filter is to be assembled.
       *
       * \param[in] cubature_factory
       * A cubature factory for integration.
       *
       * \warning This function does not work for global mean filters!
       */
      template<typename MemType_, typename DataType_, typename IndexType_, typename Space_, typename CubatureFactory_>
      static void assemble(
        LAFEM::MeanFilter<MemType_, DataType_, IndexType_>& filter,
        const Space_& space, const CubatureFactory_& cubature_factory)
      {
        // allocate primal and dual vectors
        LAFEM::DenseVector<MemType_, DataType_, IndexType_> vec_prim, vec_dual;

        // assemble vectors
        assemble(vec_prim, vec_dual, space, cubature_factory);

        // create the filter
        filter = LAFEM::MeanFilter<MemType_, DataType_, IndexType_>(std::move(vec_prim), std::move(vec_dual));
      }
    }; // class MeanFilterAssembler
  } // namespace Assembly
} // namespace FEAT

#endif // KERNEL_ASSEMBLY_MEAN_FILTER_ASSEMBLER_HPP
